/*
 * TMap Designer - A Thematic Map design software
 * Copyright (C) 2006 World Health Organization
 * http://www.who.int
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package who.tmap.toolkit.shape.annotation.impl;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.font.GlyphVector;
import java.awt.font.LineMetrics;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import who.tmap.util.MiscHelper;

public class TextRenderingHelper {

	public static final boolean ADD_MARGINS =true;
	public static final boolean NO_MARGINS =!ADD_MARGINS;
	
    // 10% of legend size for the legend margins
	public static final double LEGEND_MARGIN =0.1d;
	private static final String LFCR = "\r\n";
	// never rendered, only used to get sizing information
	private static final String TEST_STRING ="bonjour";
	
	private Font scaledFont, font;
	private double lineHeight;
	private double ascent, descent, leading;
	private double screenToMapFactor;
	private double zoomFactor;
	double textShapeFactor;
	
	private List<TextRenderingTextLine> textLines;
	private Rectangle2D textBounds;
	private Point2D pt;
	
	public TextRenderingHelper( double screenToMapFactor, double zoomFactor ) {
		this.screenToMapFactor =screenToMapFactor;
		this.zoomFactor =zoomFactor;
	}
	
	public List<TextRenderingTextLine> getTextLines() {
		return textLines;
	}
	
	public Rectangle2D getBounds() {
		return textBounds;
	}
	
	public double getLineHeight() {
		return lineHeight;
	}
	
	public double getLeading() {
		return leading;
	}
	
	public double getAscent() {
		return ascent;
	}
	
	public double getYOffsetForLine( int lineCt ) {
		return ( lineHeight * lineCt ) - ascent;
	}	
	
	private void makeFont( Graphics2D g, String fontName, int fontStyle, int fontSize ) {
		font = new Font( fontName, fontStyle, fontSize );
		
		LineMetrics lineMetrics =font.getLineMetrics( TEST_STRING, g.getFontRenderContext() );
		lineHeight =lineMetrics.getHeight();
		ascent =lineMetrics.getAscent();
		descent =lineMetrics.getDescent();
		leading =lineMetrics.getLeading();
		
		// Special case for TextShape where map factor has to be overriden
		// because we want font size to be independent from map factor 
		AffineTransform at =new AffineTransform();
		textShapeFactor =screenToMapFactor * zoomFactor / 100d;
		at.scale( textShapeFactor, textShapeFactor );
		
		// STL 2006.09.14
		// For some reason, LineMetrics does seem to work correctly with derived fonts and AffineTransformation
		// so we do the transformation manually !!
		lineHeight*= textShapeFactor;
		ascent*= textShapeFactor;
		descent*= textShapeFactor;
		leading*= textShapeFactor;		
		
		scaledFont =font.deriveFont( at );	

	}
	
	private Rectangle2D prepareTextLineForRendering( Graphics2D g, String str, int lineCt ) {
		Rectangle2D lineBounds =null;
		
		if ( !MiscHelper.isMacOS() ) {
			TextLayout textLayout =new TextLayout( str, scaledFont, g.getFontRenderContext() );
	
			TextRenderingTextLine textLine =new TextRenderingTextLine( textLayout
																	 , pt.getX()
																	 , pt.getY() + ( lineCt * ( lineHeight + leading ) ) );
			textLines.add( textLine );
			
			lineBounds =textLayout.getBounds();	
			
			textBounds.setFrame( pt.getX(), pt.getY() - ascent
							   , textBounds.getWidth() > lineBounds.getWidth() ? textBounds.getWidth() : lineBounds.getWidth()
							   , textBounds.getHeight() + ( lineHeight + leading ) );
			
		} else {
			GlyphVector gv = scaledFont.createGlyphVector( g.getFontRenderContext(), str);
			gv.performDefaultLayout();
			TextRenderingTextLine textLine =new TextRenderingTextLine( gv
																	 , pt.getX()
																	 , pt.getY() + ( lineCt * ( lineHeight + leading ) ) );
			textLines.add( textLine );
			
			lineBounds =font.getStringBounds(str, g.getFontRenderContext());
			
			textBounds.setFrame( pt.getX()
				  		   	   , pt.getY() - ascent
				  		   	   , textBounds.getWidth() > lineBounds.getWidth()*textShapeFactor ? textBounds.getWidth() 
				  		   			   														   : lineBounds.getWidth()*textShapeFactor
				  		   	   , textBounds.getHeight() + ( lineHeight + leading ) );
		}
		
		return lineBounds;
	}
	
	public void init( Graphics2D g, String fontName, int fontStyle, int fontSize ) {
		//scaledFont =
		makeFont( g, fontName, fontStyle, fontSize );
	}
	
	public Rectangle2D prepareTextForRendering( Graphics2D g, List<String> lines, Point2D pt, boolean margins ) {
		this.pt =pt;
		
		textLines =new ArrayList<TextRenderingTextLine>();
		textBounds =new Rectangle2D.Double( pt.getX(), pt.getY(), 0d, 0d );
		
		int lineCt =0;
		
        for( String line : lines ) {
        	prepareTextLineForRendering( g, line, lineCt++ );
        }		
        
        if ( margins == ADD_MARGINS ) {
        	addMargins( textBounds );
        }
		
		return textBounds;		
	}
	
	public Rectangle2D prepareTextForRendering( Graphics2D g, String text, Point2D pt, boolean margins ) {
		List<String> lines =new ArrayList<String>();

		StringTokenizer st =new StringTokenizer( text, LFCR );
		if ( st.countTokens() > 0 ) {		
			while ( st.hasMoreTokens() ) {
				lines.add( st.nextToken() );
			}
		} 		
	    
	    return prepareTextForRendering( g, lines, pt, margins );
	}

	public void renderText( Graphics2D g, boolean transparentBackground, Color backgroundColor ) {
		
		if ( !transparentBackground ) {
			eraseBackground( g, textBounds, backgroundColor );
		}
		
		for( TextRenderingTextLine entry : textLines ) {
			entry.draw( g );
		}
	}
	
	public void addMargins( Rectangle2D someBounds ) {
		double widthMargin =someBounds.getWidth() * LEGEND_MARGIN;
		double heightMargin =someBounds.getHeight() * LEGEND_MARGIN;
		
		// Add some margins
		someBounds.setFrame( someBounds.getMinX() - widthMargin
						   , someBounds.getMinY() - heightMargin
				           , someBounds.getWidth() + widthMargin*2d
				           , someBounds.getHeight() + heightMargin*2d );	
	}	
	
	public void eraseBackground( Graphics2D g2, Rectangle2D someBounds, Color backgroundColor ) {
		Color savColor =g2.getColor();
		g2.setColor( backgroundColor );
		g2.draw( someBounds );
		g2.fill( someBounds );
		g2.setColor( savColor );
	}
	
}
